#include <GTL.h>
#include <GTLEngine.h>
#include "allegro_backend.h"
#include <GTL/external_libs/tinynet/tinynet.h>

#define WIN32_LEAN_AND_MEAN
#undef DrawText
#undef LoadImage
//#include <windows.h>

using namespace GTL;
using namespace GTL::Engine;

struct LowTile
{
  union
  {
    unsigned int FourInts[4];
    struct 
    {
      unsigned int Flags:8;
      unsigned int BuildingType:8;
      unsigned int TerrainType:4;
      unsigned int RestOfInt:12;

      unsigned int RestInts[3];
    };
  };
};

struct HighTile
{
  unsigned int AverageTemperature:10;
  unsigned int Polution:22;
  unsigned int TectonicActivity:4;
  unsigned int WeatherActivity:4;
  unsigned int Flags:24;
  LowTile LowTiles[256];
};

struct Planet
{
  float Tilt, Spin;
  float Size;
  unsigned int CoreData;
  uint32_t PlanetSeed;
  unsigned int Flags;
  HighTile HighTiles[20*12];

  Planet* GenerateSeededCopy();
};

struct System
{
  struct
  {
    int Position;
    int Composition;
    float Size;
  } Belts[4];
  double StarEnergy;
  unsigned int StarType;
  unsigned int Flags;
  unsigned int PosX, PosY;
  Planet Planets[8];

  void View();
};

struct Galaxy
{
  System Systems[32];
  void Generate();
};

enum
{
  FLAG_DIRTY = 1<<0,
  FLAG_FILLED_SEED = 1<<1,
};

Planet* Planet::GenerateSeededCopy()
{
  Planet* gen = new Planet;
  memcpy(gen, this, sizeof(Planet));

  GTL::Random::FastGoodRandom rng(gen->PlanetSeed);
  for (int iHighTile=0; iHighTile<20*12; iHighTile++)
  {
    HighTile& ht = gen->HighTiles[iHighTile];
    if (!(ht.Flags & FLAG_DIRTY))
    {
      /// Generate data from seed
      for (int iLowTile=0; iLowTile<256; iLowTile++)
      {
        int x = iLowTile%16, y = iLowTile/16;
        const float scale = 64.0f;
        int fy = y+(iHighTile/20)*16;
        float f = GTL::Noise::BrownianNoise2(float(x+(iHighTile%20)*16)/scale, float(fy)/scale, 5, 0.5f);
        //float f = GTL::Noise::PerlinNoise2(float(x+(iHighTile%20)*16)/32.0f, float(y+(iHighTile/20)*16)/32.0f);
        f = (f+1.0f)/2.0f;
        f *= 1.0f-fabs(float(fy-96)/288.f);
        //f = fabs(f);
        LowTile& lt = ht.LowTiles[iLowTile];
        lt.TerrainType = (uint32_t)(f*16.0f)%16;
      }
    } else
    {
      /// Leave data (brittney!) alone
    }
  }

  return gen;
}

void System::View()
{
  if (!(Flags & FLAG_FILLED_SEED))
  {
    
  }
}

void Galaxy::Generate()
{
  //GTL::Random::FastGoodRandom fgr(seed);
  //fgr::GetRandomUnsignedInteger();
}

void StepGalaxy(Galaxy* galaxy)
{
  int ltsTouched = 0;

  for (int iSystem=0; iSystem<32; iSystem++)
  {
    System& sys = galaxy->Systems[iSystem];
    if (!(sys.Flags & FLAG_DIRTY)) continue;
    
    for (int iPlanet=0; iPlanet<8; iPlanet++)
    {
      Planet& planet = sys.Planets[iPlanet];
      if (!(planet.Flags & FLAG_DIRTY)) continue;

      for (int iHighTile=0; iHighTile<20*12; iHighTile++)
      {
        HighTile& ht = planet.HighTiles[iHighTile];
        if (!(ht.Flags & FLAG_DIRTY)) continue;

        int htPolution = 0;
        for (int iLowTile=0; iLowTile<256; iLowTile++)
        {
          LowTile& lt = ht.LowTiles[iLowTile];
          if (!(lt.Flags & FLAG_DIRTY)) continue;

          ltsTouched++;
          htPolution += lt.FourInts[2];
        }
        ht.Polution += htPolution;
      }
    }
  }

  //printf("LTS Touched: %i\n", ltsTouched);
}

class MainMode : public Mode
{
public:
	Font font;
	Image image;
	Image butan;
	GUI::Button client, server, send_pack;

	Shader shader;

	Image target, droplet, window, black;
  Image cursor;

  Galaxy* mCurrentGalaxy;
  Planet* firstPlanet;

	virtual int Load()
	{
		font = Backend->LoadFont("Data/font.ttf");
    target = Backend->CreateRenderTarget(20*16, 12*16);

    cursor = Backend->LoadImage("Data/selecter_cursor.png", true);

    printf("Galaxy size: %i (%i)\n", sizeof(Galaxy), sizeof(Galaxy)/(1024*1024));
    srand(Backend->GetTimeSec());
    mCurrentGalaxy = new Galaxy;
    memset(mCurrentGalaxy, 0, sizeof(Galaxy));

    uint64_t cl = Backend->GetTimeMsec();
    StepGalaxy(mCurrentGalaxy);
    printf("Time to touch every page: %li\n", Backend->GetTimeMsec()-cl);


    size_t result;
    for (int q=0; q<3; q++)
    {
      result = 0;
      cl = Backend->GetTimeMsec();
      for (int i=0; i<10000; i++)
      {
        char temp[32];
        GTL::Strings::Format(temp, "Zazolc {0}", time(NULL));
        result += strlen(temp);
      }
      printf("Time to format: %i (%i)\n", int(Backend->GetTimeMsec()-cl), result);

      result = 0;
      cl = Backend->GetTimeMsec();
      for (int i=0; i<10000; i++)
      {
        char temp[32];
        sprintf(temp, "Zazolc %i", time(NULL));
        result += strlen(temp);
      }
      printf("Time to sprintf: %i (%i)\n", int(Backend->GetTimeMsec()-cl), result);
    }

    firstPlanet = mCurrentGalaxy->Systems[0].Planets[0].GenerateSeededCopy();

    printf("Size of LTS: %i\n", sizeof(LowTile));

    GTL::Structures::DataNodeWilson node;
    //node.Parse("{type:button, x=20, y=50, image:'Data/butan.png', font:'Data/font.ttf', label:'Yohoho and a bottle of milk!'}");
    char* tf = GTL::Files::Just::LoadEntireTextFile("Data/gui.txt");
    node.Parse(tf);
    delete[] tf;
    MainGUI.AddChild(GUI::Control::CreateControlFromNode(&node));
    //node.Parse("{ o = kurwa, na = 50, dodatek:true, obsluguje:[tablice, i, inne, pi,3,rdo1y,{zag:nie zdz one}] }");

		
		return 0;
	}

	virtual int Event(GameEvent event) /// TODO: Finish me
	{
		if (event.EventID == GUI::Event::Click && event.Caller == &server)
		{
		} else if (event.EventID == GUI::Event::Click && event.Caller == &client)
		{
		} else if (event.EventID == GUI::Event::Click && event.Caller == &send_pack)
		{
		}
		return 0;
	}

	virtual int Unload()
	{
    delete firstPlanet;
    delete mCurrentGalaxy;
		return 0;
	}

	virtual int Update(float dt)
	{
		

		return 0;
	}


	virtual int Draw(float dt)
	{
		Backend->ClearScreen(ColorRGBA(0,0,0,1));

    target->Lock();
    Backend->SetRenderTarget(target.Get());
    //Backend->LockDrawing(true);
    
    for (int ht=0; ht<20*12; ht++)
    {
      HighTile& hit = firstPlanet->HighTiles[ht];
      int hx = (ht%20)*16, hy = (ht/20)*16;
      //printf("Pixelek %ix%i\n", hx, hy);
      for (int lt=0; lt<256; lt++)
      {
        int lx = lt%16, ly = lt/16;
        float t = float(hit.LowTiles[lt].TerrainType)/15.0f;
        //ColorRGBA c = ColorRGBA(t,t,t,1);
        ColorRGBA c = ColorRGBA(0,0.8,0,1);
        if (t<0.5f) c = ColorRGBA(0,0,1,1);
        Backend->PutPixel(Point(hx+lx, hy+ly), c);
        //if (lt>16)break;
      }
    }
    //Backend->LockDrawing(false);
    Backend->SetRenderTarget(0);
    target->Unlock();

    target->Draw(Rect(0,0,320*2,192*2));

    float mx = float(int(Backend->MouseState->X)/32);
    float my = float(int(Backend->MouseState->Y)/32);
    cursor->Draw(Point(mx*32,my*32));

		return 0;
	}
};

class SummonerGame : public GTL::Engine::Game
{
public:
	virtual int StartGame()
	{
		SetNext('main');
		return 0;
	}
	virtual Mode* CreateMode(int id, int arg1, int arg2)
	{
		switch (id)
		{
		case 'main':
			return AutoMembers::Producible::Produce<MainMode>();
		}
		return 0;
	}
private:
	
};

int main()
{
	//Logging::Level = Logging::Trace;
	GTL::Engine::Backend = new AllegroBackend;
	SummonerGame game;
	GTL::Engine::Backend->Init(&game, 800, 600);
	game.Run();

	return 0;
}